package com.yourenbang.lingyun.transaction.util.yunhuisuan;

import org.apache.tomcat.util.codec.binary.Base64;

import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import java.io.ByteArrayOutputStream;
import java.nio.charset.StandardCharsets;
import java.security.InvalidKeyException;
import java.security.KeyFactory;
import java.security.NoSuchAlgorithmException;
import java.security.Signature;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;

public class RSAPKCS8Encrypt {
    /**
     * 加密算法
     */
    public static final String KEY_ALGORITHMS = "RSA";
    /**
     * 签名算法
     */
    public static final String SIGNATURE_ALGORITHM = "SHA1WithRSA";
    /**
     * RSA最大加密明文大小
     */
    private static final int MAX_ENCRYPT_BLOCK = 245;
    /**
     * RSA最大解密密文大小
     */
    private static final int MAX_DECRYPT_BLOCK = 256;

    /**
     * 从字符串中加载公钥
     *
     * @param publicKeyStr 公钥数据字符串
     * @throws Exception 加载公钥时产生的异常
     */
    public static RSAPublicKey loadPublicKey(String publicKeyStr) throws Exception {
        try {
            byte[] buffer = Base64.decodeBase64(publicKeyStr);
            KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHMS);
            X509EncodedKeySpec keySpec = new X509EncodedKeySpec(buffer);
            return (RSAPublicKey) keyFactory.generatePublic(keySpec);
        } catch (NoSuchAlgorithmException e) {
            throw new Exception("无此算法");
        } catch (InvalidKeySpecException e) {
            throw new Exception("公钥非法");
        } catch (NullPointerException e) {
            throw new Exception("公钥数据为空");
        }
    }

    public static RSAPrivateKey loadPrivateKey(String privateKeyStr) throws Exception {
        try {
            byte[] buffer = Base64.decodeBase64(privateKeyStr);
            PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(buffer);
            KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHMS);
            return (RSAPrivateKey) keyFactory.generatePrivate(keySpec);
        } catch (NoSuchAlgorithmException e) {
            throw new Exception("无此算法");
        } catch (InvalidKeySpecException e) {
            throw new Exception("私钥非法");
        } catch (NullPointerException e) {
            throw new Exception("私钥数据为空");
        }
    }

    /**
     * RSA签名
     *
     * @param content    待签名数据
     * @param privateKey 商户私钥
     * @return 签名值
     */
    public static String sign(String content, RSAPrivateKey privateKey) throws Exception {
        Signature signature = Signature.getInstance(SIGNATURE_ALGORITHM);
        signature.initSign(privateKey);
        signature.update(content.getBytes(StandardCharsets.UTF_8.name()));
        byte[] signed = signature.sign();
        return (new Base64()).encodeAsString(signed);
    }

    /**
     * RSA验签名检查
     *
     * @param content   待签名数据
     * @param sign      签名值
     * @param publicKey 分配给企业的公钥
     * @return 布尔值
     */
    public static boolean doCheck(String content, String sign, RSAPublicKey publicKey) throws Exception {
        Signature signature = Signature.getInstance(SIGNATURE_ALGORITHM);
        signature.initVerify(publicKey);
        signature.update(content.getBytes(StandardCharsets.UTF_8.name()));
        return signature.verify(new Base64().decode(sign));
    }

    /**
     * 加密过程
     *
     * @param publicKey 公钥
     * @param data      明文数据
     * @return
     * @throws Exception 加密过程中的异常信息
     */
    public static String encrypt(RSAPublicKey publicKey, byte[] data) throws Exception {
        if (publicKey == null) {
            throw new Exception("加密公钥为空, 请设置");
        }
        Cipher cipher;
        try {
            KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHMS);
            cipher = Cipher.getInstance(keyFactory.getAlgorithm());
            cipher.init(Cipher.ENCRYPT_MODE, publicKey);

            int inputLen = data.length;
            ByteArrayOutputStream out = new ByteArrayOutputStream();
            int offSet = 0;
            byte[] cache;
            int i = 0;
            // 对数据分段加密
            while (inputLen - offSet > 0) {
                if (inputLen - offSet > MAX_ENCRYPT_BLOCK) {
                    cache = cipher.doFinal(data, offSet, MAX_ENCRYPT_BLOCK);
                } else {
                    cache = cipher.doFinal(data, offSet, inputLen - offSet);
                }
                out.write(cache, 0, cache.length);
                i++;
                offSet = i * MAX_ENCRYPT_BLOCK;
            }
            byte[] encryptedData = out.toByteArray();
            out.close();
            return (new Base64()).encodeToString(encryptedData);
        } catch (NoSuchAlgorithmException e) {
            throw new Exception("无此加密算法");
        } catch (NoSuchPaddingException e) {
            e.printStackTrace();
            return null;
        } catch (InvalidKeyException e) {
            throw new Exception("加密公钥非法,请检查");
        } catch (IllegalBlockSizeException e) {
            throw new Exception("明文长度非法");
        } catch (BadPaddingException e) {
            throw new Exception("明文数据已损坏");
        }
    }

    /**
     * 解密过程
     *
     * @param privateKey    私钥
     * @param encryptedData 密文数据
     * @return 明文
     * @throws Exception 解密过程中的异常信息
     */
    public static byte[] decrypt(RSAPrivateKey privateKey, byte[] encryptedData) throws Exception {
        if (privateKey == null) {
            throw new Exception("解密私钥为空, 请设置");
        }
        Cipher cipher;
        try {
            KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHMS);
            cipher = Cipher.getInstance(keyFactory.getAlgorithm());
            cipher.init(Cipher.DECRYPT_MODE, privateKey);
            int inputLen = encryptedData.length;
            ByteArrayOutputStream out = new ByteArrayOutputStream();
            int offSet = 0;
            byte[] cache;
            int i = 0;
            // 对数据分段解密
            while (inputLen - offSet > 0) {
                if (inputLen - offSet > MAX_DECRYPT_BLOCK) {
                    cache = cipher.doFinal(encryptedData, offSet, MAX_DECRYPT_BLOCK);
                } else {
                    cache = cipher.doFinal(encryptedData, offSet, inputLen - offSet);
                }
                out.write(cache, 0, cache.length);
                i++;
                offSet = i * MAX_DECRYPT_BLOCK;
            }
            byte[] decryptedData = out.toByteArray();
            out.close();
            return decryptedData;
        } catch (NoSuchAlgorithmException e) {
            throw new Exception("无此解密算法");
        } catch (NoSuchPaddingException e) {
            e.printStackTrace();
            return null;
        } catch (InvalidKeyException e) {
            throw new Exception("解密私钥非法,请检查");
        } catch (IllegalBlockSizeException e) {
            throw new Exception("密文长度非法");
        } catch (BadPaddingException e) {
            e.printStackTrace();
            throw new Exception("密文数据已损坏");
        }
    }

    public static void main(String[] args) {
//        try {
//            String publicKey = "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAnCyX7UNF0JehRJmm8W1BpacKFKfJVgYGFJ6PpQKbfmx3AePuRYPerBKTfBgaLlsMvY525a0hslAKknTWuRzJln2/ZNjt0DWVgShL0/oP5JmHTrC/krmlLZkfY4dRnF0Qrtnh7mpYbjrzOnDISP7dFckhK9BPKnhZCisSVTUB/xQTdv/8gbSi5yGHlvfaIBZiPdMYsVxr8+KP3cy/yoacyoh8zUL7HjVKQirmPUGRBucYGreRbLSSH8UhR6F8GxriJ12QMNRMVhAHCCj+8gI/LS3JK+6pnuw8uhPK/hnvWz2oIFc1m6u5nBHkYFNRzu4sb9J2y7vRwSv8r8gCQmN2AQIDAQAB";
//            String privateKey = "MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCcLJftQ0XQl6FEmabxbUGlpwoUp8lWBgYUno+lApt+bHcB4+5Fg96sEpN8GBouWwy9jnblrSGyUAqSdNa5HMmWfb9k2O3QNZWBKEvT+g/kmYdOsL+SuaUtmR9jh1GcXRCu2eHualhuOvM6cMhI/t0VySEr0E8qeFkKKxJVNQH/FBN2//yBtKLnIYeW99ogFmI90xixXGvz4o/dzL/KhpzKiHzNQvseNUpCKuY9QZEG5xgat5FstJIfxSFHoXwbGuInXZAw1ExWEAcIKP7yAj8tLckr7qme7Dy6E8r+Ge9bPaggVzWbq7mcEeRgU1HO7ixv0nbLu9HBK/yvyAJCY3YBAgMBAAECggEAKILIxhr1OS6b/pd83pNgu/+gdcswRsg5O5AF4t/SRfEMzci6Pf3+/NO0vjPiVrP3khQqc1l9rCtnEqaMLGhzQj5ML3S1+5Rqu54gTVRIWxwYJSlkCENvvWxC4CZx3QWnFjQejyG23qEcBsTKr2StJMGPgTX/0xx0vFZ/4uplIi1C7vMkHMwZQMIAgH48T59FBJTwl6yskUYbj3aOsQK4471iaDctOG4UuVSj9Ian3KeMeen5BNF1Yw8EghXTtoEdVNvGSne2lT/5BsKyScJywpfBfiycR+bdppmaw4KLQXn6ZQnbc8hvDOtX1ZhUv5vMBkt9GLKgXf4H+Saiajy3gQKBgQDYiHFcorqPH6ClTqFDPR4rgxkE7noILcP7QxBwa0I1f2ROkqMqvNPz4bJ3hNGveBmq7QW0iMnYCoNVKjO9PpWuMntNoRhbYXrSz8BReTGyA7IIOte2+mAEKTWu5rY80AM5HztWqY2I7ddmDFcVs3B9zSs2ufaMIOEiFz1v3S7k/QKBgQC4o8YdhdNQqg9C3MbqhoPE8nmrALoWtCEJnjKE/PEhM+qfoMJtet7KR0WDqi6j8o+bqHpIIJjGD+PWN8bOdRV4DZBK5oDGO+xUhonKjwfHg62RJWcGF08PCqyyPb1YnLz3JqG00QzrkgCYk5ddmV/fCHVTCm0SS93b0BllclmGVQKBgBr+V3N+7dbt0G8LMFtbgSlXfIT63cerPOVT5Pa+yFA3A2AwM5seUyXyGvd/XUyMolaGmdF5w//kv56cVjrfVHWU4X45+PlbW55wO79sZl+UylrwSvgKmnGqWYDmTg2iuOQkGpehJ33MeK/GFg0kg3+LVjnKUQwDBU3kq8oIPtJZAoGAHD5pKtDyzy8EYJz3DJ8A/xp8RA07agep2hfJ8I/krsiLRlnIBAk8lUKrvoVE1y1ejFgMRIY/+GQr3PPxRFmirBefD+YMosY903w1DbgtX3akEUTgao0cOdfPLQDGuwxW15OgPtGPoWVXE//Xsqiogqx/jfFVV+wlBt/hUqRYT3UCgYEAs5qRAZJHAj87Aq5h+HsKmpAHJo35ySWo5a0j1kic8flWkhRit67o9lwhIn3uOzekhnplht9yXX7FahYeFC8XjAMpHCbQULYDWEhVytPRpBKu8upVO3NOUrRIDpplZzlNmWfyc4abJbzm9irU68oNwziz9v4f09b4YvqYIJG76BA=";
//
//            String str = "{\"enterpriseNo\":\"8882008000002\",\"apiCode\":\"1000\",\"taskNo\":\"T490514755253960704\",\"entOrderNo\":\"E20200123456789\",\"payeeName\":\"张三\",\"payeeIdCard\":\"445224199404206312\",\"payeePhone\":\"18888888888\",\"payeeAccount\":\"1564561\",\"paymentModel\":\"100\",\"amount\":\"100\"}";
//
//            RSAPublicKey pubKey = loadPublicKey(publicKey);
//            RSAPrivateKey priKey = loadPrivateKey(privateKey);
//
//            String body = encrypt(pubKey, str.getBytes(StandardCharsets.UTF_8.name()));
//            System.out.println("公钥加密：" + body);
//
//            String bodyText = new String(decrypt(priKey, Base64.decodeBase64(body)), StandardCharsets.UTF_8.name());
//            System.out.println("私钥解密：" + bodyText);
//
//            String sign = sign(str, priKey);
//            System.out.println("私钥签名：" + sign);
//
//            boolean signCheck = doCheck(str, sign, pubKey);
//            System.out.println("公钥验签：" + signCheck);
//        } catch (UnsupportedEncodingException e) {
//            throw new RuntimeException(e);
//        } catch (Exception e) {
//            throw new RuntimeException(e);
//        }
        //String res = "{\"code\":\"000000\",\"msg\":\"成功\",\"data\":{\"entOrderNo\":\"ENT1679910010733\",\"pfOrderNo\":\"1640287597485838336\",\"applyTime\":\"2023-03-27 17:40:12\"},\"sign\":\"UekKR6CAWQR3v5Dp/VNBqwzItiNu1bUwrSKrnxeB1s/HMU6NtKnKMHWM30lna0sYHpUtDJeo3UTPpCU1SVDn/0q3Z9/dt3gh2dqpFNR8OWg48MWXS2rCSRex6w60+qs9cUG51859ZU5TZqqqmGbWIClbzEaQdFUcnxyFgk0ONhlm0ab9p8p9G5cU/Uf6majsj1PZQapRKqWQqwmfLCJRYGLzjD8reAAyRWra7lbvsqvz8oDDsy17/TCIGP8Fit6o0O+zaGEbcu8szMaLmv1Kso+06i6K3R4/QNP4JJfFFS4BJ4LISBt3uh2gJAKYpjzVhwN/rhq6fGr/b6Ipw72Ffw==\"}";
        //JsonObject
    }
}
